import javax.imageio.ImageIO;
import javax.imageio.stream.ImageOutputStream;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.*;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.util.ArrayList;

// Press Shift twice to open the Search Everywhere dialog and type `show whitespaces`,
// then press Enter. You can now see whitespace characters in your code.
public class Main {
//    public static void main(String[] args) {
//        // Press Alt+Enter with your caret at the highlighted text to see how
//        // IntelliJ IDEA suggests fixing it.
//        System.out.printf("Hello and welcome!");
//
//        // Press Shift+F10 or click the green arrow button in the gutter to run the code.
//        for (int i = 1; i <= 5; i++) {
//
//            // Press Shift+F9 to start debugging your code. We have set one breakpoint
//            // for you, but you can always add more by pressing Ctrl+F8.
//            System.out.println("i = " + i);
//        }
//    }
    private JFrame frame;
    private JPanel leftPanel;
    private JPanel rightPanel;
    private JPanel smallPanel;
    private float white = 0;
    private JButton loadFileForAddButton;
    private JButton loadFileForRePaintButton;
    private JButton greyAverageButton;
    private JButton greyNTSCButton;
    private JButton tintButton;
    private JButton addButton;
    private JButton lightButton;
    private JButton colorButton;

    private JButton saveButton;
    private JFileChooser fc;
    private Color destColor;
    private float[] destColorHSB = new float[3];
    private JFileChooser fileChooserForAdd;
    private ArrayList<OneImage> images;
//    private File[] sourceFiles;
//    private ArrayList<Image> resultImages;
    private File[] resultFiles;
    private JPanel middlePanel;
    private Image srcImageForAdd;


    private class OneImage{
        public Image srcImage;
        public File filename;
        public Image resImage;
        public OneImage(File file){
            filename = file;
            loadImage();
        }
        public void loadImage(){
            try {
                BufferedImage bufferedImage = ImageIO.read(filename);
                if (bufferedImage.getType() != BufferedImage.TYPE_INT_ARGB) {
                    BufferedImage bufferedImage2 = new BufferedImage(
                                    bufferedImage.getWidth(),
                                    bufferedImage.getHeight(),
                                    BufferedImage.TYPE_INT_ARGB
                            );
                    Graphics graphics = bufferedImage2.getGraphics();
                    graphics.drawImage(bufferedImage, 0, 0, null);
                    srcImage = bufferedImage2;
                } else {
                    srcImage = bufferedImage;
                }

            } catch (IOException e) {
            }
        }

        public void saveTransformedImage(String filenamePrefix){
            if( resImage == null )
                return;
            String newFilename =
                    filename.getParentFile().toString()
                            + FileSystems.getDefault().getSeparator()
                            + filenamePrefix
                            + filename.getName();
            System.out.println(newFilename);

            try {
                if (resImage instanceof BufferedImage) {
                    ImageIO.write((RenderedImage)resImage, "PNG",
                                new File(newFilename));
                } else {
                    // Create a buffered image with transparency
                    BufferedImage bi = new BufferedImage(
                            resImage.getWidth(null), resImage.getHeight(null),
                            BufferedImage.TYPE_INT_ARGB);

                    Graphics2D graphics2D = bi.createGraphics();
                    graphics2D.drawImage(resImage, 0, 0, null);
                    graphics2D.dispose();
                    ImageIO.write(bi, "PNG",
                            new File(newFilename));
                }

            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        public void transformImage(int type){
            int width = srcImage.getWidth(null);
            int height = srcImage.getHeight(null);
            int[] pixels = new int[width * height];

            int width2 = srcImage.getWidth(null);
            int height2 = srcImage.getHeight(null);
            int[] pixelsForAdd = new int[width2 * height2];


            try {
                PixelGrabber pg = new PixelGrabber(srcImage,
                        0, 0, width, height,
                        pixels, 0, width);
                pg.grabPixels();
                if ( type == 4 ) {
                    PixelGrabber pixelGrabber = new PixelGrabber(srcImageForAdd,
                            0, 0, width2, height2,
                            pixelsForAdd, 0, width2);
                    pixelGrabber.grabPixels();
                }
            } catch (InterruptedException e) {
                System.out.println("Failed to grab pixels");
                e.printStackTrace();
                resImage = null;
                return;
            }

            for (int p = 0; p < width * height; p++) {
                if( type == 0 )
                    pixels[p] = getPixelLighted(pixels[p], white);
                if( type == 1 )
                    pixels[p] = getPixelAverage(pixels[p]);
                if( type == 2 )
                    pixels[p] = getPixelNTSC(pixels[p]);
                if( type == 3 )
                    pixels[p] = getPixelTinted(pixels[p]);
                if( type == 4 )
                    pixels[p] = getPixelAdd(pixels[p], pixelsForAdd[p]);

//                pixels[p] = pixel; //0xFF101010;
            }

            resImage = leftPanel.createImage(new MemoryImageSource(width, height, pixels, 0, width));
        }

        private static int getPixelAverage(int srcPixel) {
            int pixel = srcPixel;
            int alpha = pixel & 0xFF000000;
            int average = ((pixel & 0x00FF0000) >> 16) +
                    ((pixel & 0x0000FF00) >> 8) +
                    ((pixel & 0x000000FF) >> 0);
            average = average / 3;
            if (average > 255)
                average = 255;
            pixel = alpha |
                    (average << 16) |
                    (average << 8) |
                    (average << 0);
            return pixel;
        }

        private static int getPixelLighted(int srcPixel, float white) {
            int alpha = srcPixel & 0xFF000000;
            Color srcColor = new Color(srcPixel);
            float[] srcHSB = new float[3];
            Color.RGBtoHSB(srcColor.getRed(),
                    srcColor.getGreen(),
                    srcColor.getBlue(),
                    srcHSB);
//            System.out.println(srcHSB[2]);
            return alpha
                    | (
                    Color.HSBtoRGB(
                            srcHSB[0],
                            srcHSB[1],
                            srcHSB[2] + white <= 1 ? srcHSB[2] + white : srcHSB[2]
                    )
                            & 0x00FFFFFF);
        }

        private static int getPixelAdd(int srcPixel, int srcPixelAdd) {
            int alpha = srcPixel & 0xFF000000;

//            System.out.println(srcPixel);
//            System.out.println(alpha);
            return alpha | (srcPixel == 0 ? srcPixelAdd : srcPixel);
        }

        private static int getPixelNTSC(int srcPixel) {
            int pixel = srcPixel;
            int alpha = pixel & 0xFF000000;
            int r = (pixel & 0x00FF0000) >> 16;
            int g = (pixel & 0x0000FF00) >> 8;
            int b = (pixel & 0x000000FF) >> 0;

            int ntsc = (int) (0.56 * g + 0.33 * r + 0.11 * b);
            if (ntsc > 255)
                ntsc = 255;

            pixel = alpha |
                    (ntsc << 16) |
                    (ntsc << 8) |
                    (ntsc << 0);

            return pixel;
        }

        private int getPixelTinted(int srcPixel) {
            // destColorHSB
            int alpha = srcPixel & 0xFF000000;
            Color srcColor = new Color(srcPixel);
            float[] srcHSB = new float[3];
            Color.RGBtoHSB(srcColor.getRed(),
                    srcColor.getGreen(),
                    srcColor.getBlue(),
                    srcHSB);
            return alpha | ( Color.HSBtoRGB(destColorHSB[0], srcHSB[1], srcHSB[2]) & 0x00FFFFFF);
        }
    }
    Main(String[] args) {
        // Invoked on the event dispatching thread.
        // Do any initialization here.

        //Create a file chooser
        fileChooserForAdd = new JFileChooser();
        fileChooserForAdd.setMultiSelectionEnabled(false);
        fileChooserForAdd.setCurrentDirectory(new File("/home/oleg/CMA/moulds"));

        fc = new JFileChooser();
        fc.setMultiSelectionEnabled(true);
        fc.setCurrentDirectory(new File("/home/oleg/CMA/moulds"));

        images = new ArrayList<>();

        // Show the UI.
        //Create and set up the window.
        frame = new JFrame("HelloWorldSwing");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setPreferredSize(new Dimension(1300, 600));
        frame.setLocationByPlatform(true); // setLocationRelativeTo(null);

        middlePanel = new JPanel();
        frame.add(middlePanel, BorderLayout.CENTER);
        middlePanel.setLayout(new BoxLayout(middlePanel, BoxLayout.PAGE_AXIS));

        addImagePanels();
        addUIButtons();
    }

    private void addImagePanels(){
        //Add the ubiquitous "Hello World" label.
//        JLabel label = new JLabel("Hello World");
//        frame.getContentPane().add(label);
        leftPanel = new JPanel(){
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(512, 512);
            }

            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Insets ins = getInsets();
                if( images != null ) { //&& !sourceImages.isEmpty()){
                    int x = ins.left;
                    int y = ins.top;
                    for (OneImage img : images) {
                        g.drawImage(img.srcImage,
                                x, y, x+64, y+64,
                                0, 0,
                                img.srcImage.getWidth(null),
                                img.srcImage.getHeight(null), null);
                        y = x + 64 >= 512 ? y + 64 : y;
                        x = x + 64 < 512 ? x + 64 : ins.left;

                    }
//                    g.drawImage(sourceImage,
//                            ins.left, ins.top, 256-ins.left, 256-ins.top,
//                            0, 0, 16, 16, null);
                }
            }

        };
//        leftPanel.setBorder(BorderFactory.createLineBorder(Color.RED,1));
        frame.add(leftPanel, BorderLayout.WEST);

        rightPanel = new JPanel(){
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(512, 512);
            }
            @Override
            protected void paintComponent(Graphics graphics) {
                super.paintComponent(graphics);
                Insets ins = getInsets();
                if( images != null) {
                    int x = ins.left;
                    int y = ins.top;
                    for (OneImage image : images) {
                        graphics.drawImage(image.resImage,
                                x, y, x+64, y+64,
                                0, 0, 16, 16, null);
                        y = x + 64 >= 512 ? y + 64 : y;
                        x = x + 64 < 512 ? x + 64 : ins.left;

                    }
                }

            }
        };
//        rightPanel.setBorder(BorderFactory.createLineBorder(Color.GREEN,1));
        frame.add(rightPanel, BorderLayout.EAST);



        smallPanel = new JPanel(){
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(64, 64);
            }

            @Override
            protected void paintComponent(Graphics graphics) {
                super.paintComponent(graphics);
                Insets ins = getInsets();
                if( srcImageForAdd != null) {
                    int x = ins.left;
                    int y = ins.top;

                    graphics.drawImage(srcImageForAdd,
                                x, y, x+64, y+64,
                                0, 0, 16, 16, null);



                }
//                    g.drawImage(sourceImage,
//                            ins.left, ins.top, 256-ins.left, 256-ins.top,
//                            0, 0, 16, 16, null);
            }


        };
//        smallPanel.setBorder(BorderFactory.createLineBorder(Color.RED,1));
        middlePanel.add(smallPanel, BorderLayout.CENTER);
    }

    private boolean buttonLoadFileForAddAction(ActionEvent event){
        fc.setMultiSelectionEnabled(false);
        int returnVal = fc.showOpenDialog(frame);

        if (returnVal == JFileChooser.APPROVE_OPTION) {

            File filename = fc.getSelectedFile();
            try {
                BufferedImage bufferedImage = ImageIO.read(filename);
                if (bufferedImage.getType() != BufferedImage.TYPE_INT_ARGB) {
                    BufferedImage bufferedImage2 = new BufferedImage(
                            bufferedImage.getWidth(),
                            bufferedImage.getHeight(),
                            BufferedImage.TYPE_INT_ARGB
                    );
                    Graphics graphics = bufferedImage2.getGraphics();
                    graphics.drawImage(bufferedImage, 0, 0, null);
                    srcImageForAdd = bufferedImage2;
                } else {
                    srcImageForAdd = bufferedImage;
                }

            } catch (IOException e) {
            }
            smallPanel.repaint();

            //This is where a real application would open the file.
//                log.append("Opening: " + file.getName() + "." + newline);
        }
        return false;
    }
    private boolean buttonLoadFileForRePaintAction(ActionEvent event){
        fc.setMultiSelectionEnabled(true);
        int returnVal = fc.showOpenDialog(frame);

        if (returnVal == JFileChooser.APPROVE_OPTION) {
            images.clear();
            for (File f: fc.getSelectedFiles()) {
                images.add(new OneImage(f));
            }
            leftPanel.repaint();

            //This is where a real application would open the file.
//                log.append("Opening: " + file.getName() + "." + newline);
        }
        return false;
    }

    private boolean buttonSaveFileAction(ActionEvent event){
        if( images == null || images.isEmpty() )
            return false;
        String filePrefix = JOptionPane.showInputDialog("Enter file prefix");
        if( filePrefix == null || filePrefix.isEmpty() )
            return false;
        for (OneImage image : images) {
            image.saveTransformedImage(filePrefix);
        }
        return false;
    }

    private boolean buttonGreyAverageAction(ActionEvent event){
        for (OneImage image : images) {
            image.transformImage(1);
        }
//            resultImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
//            Graphics g = resultImage.getGraphics();
//            g.drawImage(sourceImage, 0, 0, null);
        if( destColor!= null ){

        }
        rightPanel.repaint();
        return false;
    }

    private boolean buttonForAddAction(ActionEvent event){
        for (OneImage image : images) {
            image.transformImage(4);
        }
        rightPanel.repaint();
        return false;
    }

    private boolean buttonLightAction(ActionEvent event){
        white = Float.parseFloat(JOptionPane.showInputDialog("Enter white"));
        for (OneImage image : images) {
            image.transformImage(0);
        }
        rightPanel.repaint();
        return false;
    }

    private boolean buttonGreyNTSCAction(ActionEvent event){
        for (OneImage image : images) {
            image.transformImage(2);
        }
        rightPanel.repaint();
        return false;
    }

    private boolean buttonTintAction(ActionEvent event){
        if( destColor == null ){
            JOptionPane.showMessageDialog(frame, "Please choose a tint color");
            return false;
        }

        for (OneImage image : images) {
            image.transformImage(3);
        }
        rightPanel.repaint();
        return false;
    }

    private void addUIButtons(){

        loadFileForRePaintButton = new JButton("Load file for repaint...");
        middlePanel.add(loadFileForRePaintButton, BorderLayout.CENTER);
        loadFileForRePaintButton.addActionListener(this::buttonLoadFileForRePaintAction);


        loadFileForAddButton = new JButton("Load file for add...");
        middlePanel.add(loadFileForAddButton, BorderLayout.CENTER);
        loadFileForAddButton.addActionListener(this::buttonLoadFileForAddAction);

        addButton = new JButton("Add image");
        middlePanel.add(addButton, BorderLayout.SOUTH);
        addButton.addActionListener(this::buttonForAddAction);


        colorButton = new JButton("Choose color ...");
        middlePanel.add(colorButton, BorderLayout.CENTER);
        colorButton.addActionListener(event ->
        {
            destColor = JColorChooser.showDialog(null, "Choose color", Color.BLACK);
            if( destColor != null )
                Color.RGBtoHSB(destColor.getRed(),
                        destColor.getGreen(),
                        destColor.getBlue(),
                        destColorHSB);
        });

        lightButton = new JButton("Light images");
        middlePanel.add(lightButton, BorderLayout.SOUTH);
        lightButton.addActionListener(this::buttonLightAction);

        greyAverageButton = new JButton("Gray average");
        middlePanel.add(greyAverageButton, BorderLayout.SOUTH);
        greyAverageButton.addActionListener(this::buttonGreyAverageAction);

        greyNTSCButton = new JButton("Gray NTSC");
        middlePanel.add(greyNTSCButton, BorderLayout.SOUTH);
        greyNTSCButton.addActionListener(this::buttonGreyNTSCAction);

        tintButton = new JButton("Tint images");
        middlePanel.add(tintButton, BorderLayout.SOUTH);
        tintButton.addActionListener(this::buttonTintAction);

        saveButton = new JButton("Save file ... ");
        middlePanel.add(saveButton, BorderLayout.SOUTH);
        saveButton.addActionListener(this::buttonSaveFileAction);

    }
    public void show() {
//        try {
////            sourceImage = ImageIO.read(new File("/home/oleg/BlockbenchWorkspace/CMA/iron/shablon/axe.png"));
//            if (sourceImage.getType() != BufferedImage.TYPE_INT_ARGB) {
//                BufferedImage bi2 =
//                        new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_INT_ARGB);
//                Graphics big = bi2.getGraphics();
//                big.drawImage(sourceImage, 0, 0, null);
//                sourceImage = bi2;
//            }
//            // leftPanel.repaint();
//        } catch (IOException e) {
//        }

        //Display the window.
        frame.pack();
        frame.setVisible(true);
    }


    public static void main(final String[] args) {
        // Schedule a job for the event-dispatching thread:
        // creating and showing this application's GUI.
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new Main(args).show();
            }
        });
    }
}